using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using OpenNETCF.IO;

namespace Accelerometer
{
    public class Accel : StreamInterfaceDriver
    {

        #region Accelerometer device IOCTL codes

        private const Int32 FILE_DEVICE_UNKNOWN = 0x00000022;
        private const Int32 FILE_ANY_ACCESS = 0x0;
        private const Int32 METHOD_BUFFERED = 0x0;

        private const Int32 CODE_IOCTL_ACC_SET_MODE =   4030;         // Set ACC Mode
        private const Int32 CODE_IOCTL_ACC_SET_GSEL =   4031;         // Set ACC G-level
        private const Int32 CODE_IOCTL_ACC_GET_MODE =   4060;         // Get ACC Mode
        private const Int32 CODE_IOCTL_ACC_GET_GSEL =   4061;         // Get ACC G-level
        private const Int32 CODE_IOCTL_ACC_GET_OUTPUT = 4062;         // Get G-data


        private static byte[] inbuffer = new byte[2];
        private static byte[] outbuffer = new byte[Marshal.SizeOf(typeof(XYZGForce))];

        private const Int32 IOCTL_ACC_SET_MODE =
            ((FILE_DEVICE_UNKNOWN) << 16) | ((FILE_ANY_ACCESS) << 14)
            | ((CODE_IOCTL_ACC_SET_MODE) << 2) | (METHOD_BUFFERED);

        private const Int32 IOCTL_ACC_SET_GSEL =
            ((FILE_DEVICE_UNKNOWN) << 16) | ((FILE_ANY_ACCESS) << 14)
            | ((CODE_IOCTL_ACC_SET_GSEL) << 2) | (METHOD_BUFFERED);

        private const Int32 IOCTL_ACC_GET_MODE =
            ((FILE_DEVICE_UNKNOWN) << 16) | ((FILE_ANY_ACCESS) << 14)
            | ((CODE_IOCTL_ACC_GET_MODE) << 2) | (METHOD_BUFFERED);

        private const Int32 IOCTL_ACC_GET_GSEL =
            ((FILE_DEVICE_UNKNOWN) << 16) | ((FILE_ANY_ACCESS) << 14)
            | ((CODE_IOCTL_ACC_GET_GSEL) << 2) | (METHOD_BUFFERED);

        private const Int32 IOCTL_ACC_GET_OUTPUT =
            ((FILE_DEVICE_UNKNOWN) << 16) | ((FILE_ANY_ACCESS) << 14)
            | ((CODE_IOCTL_ACC_GET_OUTPUT) << 2) | (METHOD_BUFFERED);


        #endregion

        #region Native interface structures

        [StructLayout(LayoutKind.Sequential)]
        public struct XYZGForce
        {
            public Int16 X;
            public Int16 Y;
            public Int16 Z;
        }

        public enum GSEL
        {
            R2G,
            R4G,
            R8G
        }

        [Flags]
        public enum MODE : ushort
        {
            Xen = 0x0001,
            Yen = 0x0002,
            Zen = 0x0004,
        }



        #endregion

        #region ctor / dtor
        /// <summary>
        /// Provides access to the Accelerometer.
        /// </summary>
        public Accel() : base("ACC1:")
        {
            // open the driver
            Open(FileAccess.ReadWrite, FileShare.ReadWrite);
        }

        ~Accel()
        {
            // close the driver
            Close();
        }
        #endregion


        public XYZGForce gForce
        {
            get
            {
                XYZGForce xyz = new XYZGForce();

                try
                {
                    this.DeviceIoControl(IOCTL_ACC_GET_OUTPUT, null, outbuffer);
                    xyz = (XYZGForce)DeserializeFromByteArray(outbuffer, typeof(XYZGForce));
                }
                catch (Exception)
                {
                    throw new Exception("Unable to complete native request:" + Marshal.GetLastWin32Error());
                }
                return xyz;
            }
        }

        public GSEL gsel
        {
            get
            {
                GSEL gsel;

                byte[] outbuffer = new byte[Marshal.SizeOf(typeof(GSEL))];
                
                try
                {
                    this.DeviceIoControl(IOCTL_ACC_GET_GSEL, null, outbuffer);
                    gsel = (GSEL)DeserializeFromByteArray(outbuffer, typeof(GSEL));
                }
                catch (Exception)
                {
                    throw new Exception("Unable to complete native request:" + Marshal.GetLastWin32Error());
                }
                return gsel;
            }
            set
            {
                try
                {
                    this.DeviceIoControl(IOCTL_ACC_SET_GSEL, SerializeToByteArray(value), null);
                }
                catch (Exception)
                {
                    throw new Exception("Unable to complete native request:" + Marshal.GetLastWin32Error());
                }
            }
        }

        // do not use ...not working yet
        public MODE mode
        {
            get
            {
                MODE mode;

                byte[] outbuffer = new byte[Marshal.SizeOf(typeof(MODE))];

                try
                {
                    this.DeviceIoControl(IOCTL_ACC_GET_MODE, null, outbuffer);
                    mode = (MODE)DeserializeFromByteArray(outbuffer, typeof(MODE));
                }
                catch (Exception)
                {
                    throw new Exception("Unable to complete native request:" + Marshal.GetLastWin32Error());
                }
                return mode;
            }
            set
            {
                try
                {
                    this.DeviceIoControl(IOCTL_ACC_SET_MODE, SerializeToByteArray(value), null);
                }
                catch (Exception)
                {
                    throw new Exception("Unable to complete native request:" + Marshal.GetLastWin32Error());
                }
            }
        }


        #region P/Invoke helpers

        /// <summary>
        /// Byte array serializer
        /// </summary>
        /// <param name="anything"></param>
        /// <returns></returns>
        private static byte[] SerializeToByteArray(object anything)
        {
            int rawsize = Marshal.SizeOf(anything);
            IntPtr buffer = Marshal.AllocHGlobal(rawsize);
            Marshal.StructureToPtr(anything, buffer, false);
            byte[] rawdatas = new byte[rawsize];
            Marshal.Copy(buffer, rawdatas, 0, rawsize);
            Marshal.FreeHGlobal(buffer);
            return rawdatas;
        }

        /// <summary>
        /// De-serializer from byte array
        /// </summary>
        /// <param name="rawdatas"></param>
        /// <param name="anytype"></param>
        /// <returns></returns>
        private static object DeserializeFromByteArray(byte[] rawdatas, Type anytype)
        {
            int rawsize = Marshal.SizeOf(anytype);
            if (rawsize > rawdatas.Length)
                return null;
            IntPtr buffer = Marshal.AllocHGlobal(rawsize);
            Marshal.Copy(rawdatas, 0, buffer, rawsize);
            object retobj = Marshal.PtrToStructure(buffer, anytype);
            Marshal.FreeHGlobal(buffer);
            return retobj;
        }
        #endregion
    }
}
